summaryrefslogtreecommitdiff
path: root/app/api/swp/download/[fileId]
diff options
context:
space:
mode:
Diffstat (limited to 'app/api/swp/download/[fileId]')
-rw-r--r--app/api/swp/download/[fileId]/route.ts160
1 files changed, 39 insertions, 121 deletions
diff --git a/app/api/swp/download/[fileId]/route.ts b/app/api/swp/download/[fileId]/route.ts
index 3af560aa..fe422015 100644
--- a/app/api/swp/download/[fileId]/route.ts
+++ b/app/api/swp/download/[fileId]/route.ts
@@ -1,9 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
-import * as fs from "fs/promises";
-import * as path from "path";
-import { eq } from "drizzle-orm";
-import db from "@/db/db";
-import { swpDocumentFiles } from "@/db/schema/SWP/swp-documents";
+import { downloadDocumentFile } from "@/lib/swp/document-service";
import { debugLog, debugError, debugSuccess } from "@/lib/debug-utils";
// API Route 설정
@@ -11,116 +7,67 @@ export const runtime = "nodejs";
export const maxDuration = 60; // 1분 타임아웃
/**
- * GET /api/swp/download/[fileId]
- * 파일 다운로드 (바이너리 직접 전송)
+ * GET /api/swp/download/[ownDocNo]?projNo=xxx&fileName=xxx
+ * 파일 다운로드 (Full API 기반)
+ *
+ * Query Parameters:
+ * - projNo: 프로젝트 번호
+ * - fileName: 파일명
*/
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ fileId: string }> }
) {
try {
- const { fileId: fileIdString } = await params;
- const fileId = parseInt(fileIdString, 10);
-
- if (isNaN(fileId)) {
- return NextResponse.json(
- { success: false, error: "잘못된 파일 ID입니다." },
- { status: 400 }
- );
- }
-
- debugLog(`[download] 다운로드 시작`, { fileId });
+ const { fileId: ownDocNo } = await params;
+ const { searchParams } = new URL(request.url);
+ const projNo = searchParams.get("projNo");
+ const fileName = searchParams.get("fileName");
- // 1. 파일 정보 조회
- const fileInfo = await db
- .select({
- FILE_NM: swpDocumentFiles.FILE_NM,
- FLD_PATH: swpDocumentFiles.FLD_PATH,
- })
- .from(swpDocumentFiles)
- .where(eq(swpDocumentFiles.id, fileId))
- .limit(1);
+ debugLog(`[download] 다운로드 시작`, { projNo, ownDocNo, fileName });
- if (!fileInfo || fileInfo.length === 0) {
- debugError(`[download] 파일 정보 없음`, { fileId });
+ // 파라미터 검증
+ if (!projNo || !ownDocNo || !fileName) {
+ debugError(`[download] 필수 파라미터 누락`, { projNo, ownDocNo, fileName });
return NextResponse.json(
- { success: false, error: "파일 정보를 찾을 수 없습니다." },
- { status: 404 }
- );
- }
-
- const { FILE_NM, FLD_PATH } = fileInfo[0];
- debugLog(`[download] 파일 정보 조회 완료`, { FILE_NM, FLD_PATH });
-
- if (!FLD_PATH || !FILE_NM) {
- debugError(`[download] 파일 경로 또는 이름 없음`, { FILE_NM, FLD_PATH });
- return NextResponse.json(
- { success: false, error: "파일 경로 또는 파일명이 없습니다." },
- { status: 404 }
- );
- }
-
- // 2. NFS 마운트 경로 확인
- const nfsBasePath = process.env.SWP_MOUNT_DIR;
- if (!nfsBasePath) {
- debugError(`[download] SWP_MOUNT_DIR 환경변수가 설정되지 않았습니다.`);
- return NextResponse.json(
- { success: false, error: "서버 설정 오류: NFS 경로가 설정되지 않았습니다." },
- { status: 500 }
+ {
+ success: false,
+ error: "필수 파라미터가 누락되었습니다. (projNo, ownDocNo, fileName)"
+ },
+ { status: 400 }
);
}
- // 3. 전체 파일 경로 생성
- const normalizedFldPath = FLD_PATH.replace(/\\/g, '/');
- const fullPath = path.join(nfsBasePath, normalizedFldPath, FILE_NM);
+ // document-service의 downloadDocumentFile 사용
+ const result = await downloadDocumentFile(projNo, ownDocNo, fileName);
- debugLog(`[download] 파일 경로`, {
- fileId,
- FILE_NM,
- FLD_PATH,
- normalizedFldPath,
- fullPath,
- });
-
- // 4. 파일 존재 여부 확인
- try {
- await fs.access(fullPath, fs.constants.R_OK);
- } catch (accessError) {
- debugError(`[download] 파일 접근 불가`, { fullPath, error: accessError });
+ if (!result.success || !result.data) {
+ debugError(`[download] 다운로드 실패`, { error: result.error });
return NextResponse.json(
- { success: false, error: `파일을 찾을 수 없습니다: ${FILE_NM}` },
+ {
+ success: false,
+ error: result.error || "파일 다운로드 실패"
+ },
{ status: 404 }
);
}
- // 5. 파일 읽기
- debugLog(`[download] 파일 읽기 시작`, { fullPath });
- const fileBuffer = await fs.readFile(fullPath);
-
- debugLog(`[download] 파일 Buffer 읽기 완료`, {
- bufferLength: fileBuffer.length,
- isBuffer: Buffer.isBuffer(fileBuffer),
- bufferType: typeof fileBuffer,
- constructor: fileBuffer.constructor.name,
- first20Bytes: fileBuffer.slice(0, 20).toString('hex')
- });
-
- // 6. MIME 타입 결정
- const mimeType = getMimeType(FILE_NM);
-
debugSuccess(`[download] 다운로드 성공`, {
- fileName: FILE_NM,
- size: fileBuffer.length,
- mimeType,
+ fileName: result.fileName,
+ size: result.data.length,
+ mimeType: result.mimeType,
});
- // 7. 바이너리 응답 반환
- return new NextResponse(fileBuffer, {
+ // Uint8Array를 Buffer로 변환
+ const buffer = Buffer.from(result.data);
+
+ // 바이너리 응답 반환
+ return new NextResponse(buffer, {
status: 200,
headers: {
- "Content-Type": mimeType,
- "Content-Disposition": `attachment; filename="${encodeURIComponent(FILE_NM)}"`,
- "Content-Length": String(fileBuffer.length),
+ "Content-Type": result.mimeType || "application/octet-stream",
+ "Content-Disposition": `attachment; filename="${encodeURIComponent(result.fileName || fileName)}"`,
+ "Content-Length": String(buffer.length),
},
});
} catch (error) {
@@ -140,33 +87,4 @@ export async function GET(
}
}
-/**
- * MIME 타입 결정
- */
-function getMimeType(fileName: string): string {
- const ext = path.extname(fileName).toLowerCase();
-
- const mimeTypes: Record<string, string> = {
- ".pdf": "application/pdf",
- ".doc": "application/msword",
- ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
- ".xls": "application/vnd.ms-excel",
- ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
- ".ppt": "application/vnd.ms-powerpoint",
- ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
- ".txt": "text/plain",
- ".csv": "text/csv",
- ".jpg": "image/jpeg",
- ".jpeg": "image/jpeg",
- ".png": "image/png",
- ".gif": "image/gif",
- ".zip": "application/zip",
- ".rar": "application/x-rar-compressed",
- ".7z": "application/x-7z-compressed",
- ".dwg": "application/acad",
- ".dxf": "application/dxf",
- };
-
- return mimeTypes[ext] || "application/octet-stream";
-}